package cn.tml.innermost.music.service.impl;

import cn.tml.innermost.framework.entity.enums.ResultCode;
import cn.tml.innermost.framework.exception.ServiceException;
import cn.tml.innermost.music.dos.*;
import cn.tml.innermost.music.params.MusicSearchParams;
import cn.tml.innermost.music.vo.*;
import cn.tml.innermost.music.params.MusicListParams;
import cn.tml.innermost.music.mapper.MusicMapper;
import cn.tml.innermost.music.mapper.MusicListMusicMapper;
import cn.tml.innermost.music.mapper.MusicListMapper;
import cn.tml.innermost.music.service.MusicListService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 燧枫
 * @since 2022-10-18
 */
@Service
@Slf4j
public class MusicListServiceImpl extends ServiceImpl<MusicListMapper, MusicList> implements MusicListService {

    @Resource
    MusicListMusicMapper musicListMusicMapper;

    @Resource
    MusicListMapper musicListMapper;

    @Resource
    MusicMapper musicMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Resource
    private MusicListMapper MusicListMapper;

    // 歌单的redsi前缀
    private static final String MUSIC_MusicList = "music-MusicList:";

    /**
     * 获取歌单信息
     *
     * @param MusicListId
     * @return MusicListInfoVO
     */
    @Override
    public MusicListInfoVO getMusicListInfo(Long MusicListId) {
        MusicList MusicList = null;
        if (redisTemplate.hasKey(MUSIC_MusicList + MusicListId)) {
//        如果在redis中, 直接得到歌单实体类
            MusicList = (MusicList) redisTemplate.opsForValue().get(MUSIC_MusicList + MusicListId);
        } else {
//        不在, 根据歌单id得到歌单实体类
            MusicList = this.getById(MusicListId);
//        如果查不到，直接抛异常
            if (MusicList == null)
                throw new ServiceException(ResultCode.MusicList_GET_FAIL);
//        如果删除标记为true，直接抛异常
            if (MusicList.getDeleteFlag() == true)
                throw new ServiceException(ResultCode.MusicList_GET_FAIL);
//        保存至redis中
            redisTemplate.opsForValue().set(MUSIC_MusicList + MusicListId, MusicList, 60 * 60, TimeUnit.SECONDS);
        }
//        转为MusicListInfoVO并返回
        return MusicListInfoVO.valueOf(MusicList);
    }

    /***
     * @description: 分页获取所有歌单信息
     * @param  current
     * @param  size
     * @return: MusicListPageVO
     */
    public MusicListPageVO getAllMusicListInfo(Long current, Long size) {
//        限制一波分页大小（50）
        if (size > 100)
            throw new ServiceException(ResultCode.PAGE_TOO_BIG);
        MusicListPageVO MusicListPageVO = null;
        List<Long> MusicListList = new ArrayList<>();
        List<MusicListInfoVO> MusicListInfoVOList = new ArrayList<>();
//        先判断是否在redis中
        if (redisTemplate.hasKey(MUSIC_MusicList + current + "&" + size)) {
            MusicListList = redisTemplate.opsForList().
                    range(MUSIC_MusicList + current + "&" + size, 0, -1);
//        通过list取出redis中所有实体类
            for (int i = 0; i < MusicListList.size(); i++) {
//        判断实体类是否在redis中
                if (redisTemplate.hasKey(MUSIC_MusicList + MusicListList.get(i))) {
                    MusicListInfoVOList.add(MusicListInfoVO.valueOf((MusicList) redisTemplate.opsForValue()
                            .get(MUSIC_MusicList + MusicListList.get(i))));
                }
            }
//        再取出PageVO,并封装成MusicListPageVO
            if (redisTemplate.hasKey(MUSIC_MusicList + current + "&" + size + "pageOV")) {
                MusicListPageVO = new MusicListPageVO((PageOV) redisTemplate.opsForValue()
                        .get(MUSIC_MusicList + current + "&" + size + "pageOV"), MusicListInfoVOList);
            }
//        不在redis中
        } else {
//        根据current与size直接查询
            Page<MusicList> page = new Page<>(current, size);
            this.getBaseMapper().selectPage(page, new QueryWrapper<MusicList>().eq("delete_flag", 0));
//        提取PageOV所需信息并封装
            PageOV pageOV = new PageOV();
            pageOV.setCurrent(current);
            pageOV.setSize(size);
            pageOV.setTotal(page.getTotal());
            pageOV.setPages(page.getPages());
//        将pageOV保存到redis中
            redisTemplate.opsForValue().
                    set(MUSIC_MusicList + current + "&" + size + "pageOV", pageOV, 60 * 60, TimeUnit.SECONDS);
//        将提取的所有MusicList实体类转换成MusicListInfoVO并封装到List中
            for (int i = 0; i < page.getRecords().size(); i++) {
                MusicList MusicList = page.getRecords().get(i);
                MusicListList.add(MusicList.getId());
                redisTemplate.opsForValue().
                        set(MUSIC_MusicList + MusicList.getId(), MusicList, 60 * 60, TimeUnit.SECONDS);
                MusicListInfoVOList.add(MusicListInfoVO.valueOf(MusicList));
            }
            MusicListPageVO = new MusicListPageVO(pageOV, MusicListInfoVOList);
//        将list保存至redis中
            redisTemplate.opsForList().rightPushAll(MUSIC_MusicList + current + "&" + size, MusicListList);
            redisTemplate.expire(MUSIC_MusicList + current + "&" + size, 60 * 60, TimeUnit.SECONDS);
        }
        return MusicListPageVO;
    }

    @Override
    /**
     * 获取歌单所有歌曲
     * @param MusicListId
     * @return MusicListVO
     */
    public MusicListVO getMusicListAllMusic(Long musicListId) {
        MusicListVO musicListVO = null;
//        先判断是否在redis中
        if (redisTemplate.hasKey(MUSIC_MusicList + musicListId + "AllMusic")) {
            musicListVO = (MusicListVO) redisTemplate.opsForValue().get(MUSIC_MusicList + musicListId + "AllMusic");
        } else {
//        根据标签id得到歌单实体类
            MusicList MusicList = this.getById(musicListId);
//        如果查不到，直接抛异常
            if (MusicList == null)
                throw new ServiceException(ResultCode.MusicList_GET_FAIL);
//        如果删除标记为true，直接抛异常
            if (MusicList.getDeleteFlag() == true)
                throw new ServiceException(ResultCode.MusicList_GET_FAIL);
//        从关系表中查出所有所有实体类
            QueryWrapper<MusicListMusic> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("music_list_id", musicListId);
            musicListVO = new MusicListVO(MusicListInfoVO.valueOf(MusicList), musicListMusicMapper.selectList(queryWrapper));
//        保存至redis中
            redisTemplate.opsForValue().set(MUSIC_MusicList + musicListId + "AllMusic", musicListVO, 60 * 60, TimeUnit.SECONDS);
        }
        return musicListVO;
    }

    /**
     * 新建歌单
     *
     * @param musicListParams
     * @return MusicListInfoVO
     */
    @Override
    public MusicListInfoVO addMusicList(MusicListParams musicListParams) {
//        先将MusicListParams转为实体类
        MusicList musicList = MusicList.valueOf(musicListParams);
        log.info(musicList.toString());
//        将实体类插入数据库中，如果失败，抛异常
        if (!this.save(musicList))
            throw new ServiceException(ResultCode.MusicList_SAVE_FAIL);
//        如果成功，初始化参数，转为MusicListInfoVO并返回
        musicList.setMusicNums(0);
        musicList.setPlaysNums(0);
        musicList.setCollectionNums(0);
        return MusicListInfoVO.valueOf(musicList);
    }

    /**
     * 删除歌单
     *
     * @param musicListId
     * @return MusicListInfoVO
     */
    @Override
    public void delMusicList(Long musicListId) {
//        根据歌单id得到歌单实体类
        MusicList MusicList = this.getById(musicListId);
//        如果查不到，直接抛异常
        if (MusicList == null)
            throw new ServiceException(ResultCode.MusicList_GET_FAIL);
//        将删除标记至为true，并更新至数据库中，失败，抛异常
//        MusicList.setDeleteFlag(true);
        QueryWrapper<MusicList> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id",musicListId);
        if(musicListMapper.delete(queryWrapper) == 1){
            return;
        }else {
            throw new ServiceException(ResultCode.MusicList_UPDATE_FAIL);
        }

//        如果成功，转为MusicListInfoVO并返回
    }

    /***
     * 修改歌单信息
     * @param musicListParams
     * @return: MusicListInfoVO
     */
    @Override
    public MusicListInfoVO updateMusicList(MusicListParams musicListParams) {
        Long musicListId = musicListParams.getMusicListId();
//        根据歌单id得到歌单实体类
        MusicList MusicList = this.getById(musicListId);
//        如果查不到，直接抛异常
        if (MusicList == null)
            throw new ServiceException(ResultCode.MusicList_GET_FAIL);
//        如果删除标记为true，直接抛异常
        if (MusicList.getDeleteFlag())
            throw new ServiceException(ResultCode.MusicList_GET_FAIL);
//        将实体类同步至MusicListParams，并更新至数据库中
        MusicList.setId(musicListId);
        MusicList.setName(musicListParams.getName());
        MusicList.setAuthorName(musicListParams.getAuthorName());
        MusicList.setCoverUrl(musicListParams.getCoverUrl());
        MusicList.setDescription(musicListParams.getDescription());
        if (!updateById(MusicList))
            throw new ServiceException(ResultCode.MusicList_UPDATE_FAIL);
        if (redisTemplate.hasKey(MUSIC_MusicList + musicListId)) {
//        如果在redis中, 先将将redsi中的album对象删除
            if (!redisTemplate.delete(MUSIC_MusicList + musicListId))
                throw new ServiceException(ResultCode.ALBUM_UPDATE_FAIL);
//        再将新的对象放进redis中
            redisTemplate.opsForValue().set(MUSIC_MusicList + musicListId, MusicList, 60 * 60, TimeUnit.SECONDS);
        }
        return MusicListInfoVO.valueOf(MusicList);
    }

    @Override
    public List<MusicListInfoVO> selectMusicList(MusicSearchParams musicSearchParams) {
        List<MusicListInfoVO> res =new ArrayList<>();
        String keyWords = musicSearchParams.getKeyWords();
        int startPage = musicSearchParams.getPageIndex();
        int resultNums = musicSearchParams.getResultNums();
        Page<MusicList> page = new Page<>(startPage, resultNums);
        QueryWrapper<MusicList> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name",keyWords);
        Page<MusicList> musicListPage = musicListMapper.selectPage(page, queryWrapper);
        List<MusicList> records = musicListPage.getRecords();
        for (int i = 0; i < records.size(); i++) {
            res.add(MusicListInfoVO.valueOf(records.get(i)));
        }
        return res;
    }


    /**
     * 给歌单添加歌曲
     *
     * @param musicId,musicListId
     * @return MusicListInfoVO
     */
    @Transactional
    public MusicListInfoVO addMusicToMusicList(Long musicId, Long musicListId) {
//        根据歌单id得到歌单实体类
        MusicList musicList = this.getById(musicListId);
//            根据歌曲id得到歌曲实体类
        Music music = musicMapper.selectById(musicId);
        if (music == null || music.getDeleteFlag())
            throw new ServiceException(ResultCode.MUSIC_GET_FAIL);

        if (musicList == null || musicList.getDeleteFlag())
            throw new ServiceException(ResultCode.MusicList_GET_FAIL);

        MusicListMusic musicListMusic = new MusicListMusic();
        QueryWrapper<MusicListMusic> queryWrapper = new QueryWrapper<>();

//            将歌曲实体类转为MusicMusicList实体类

            musicListMusic.setMusicListId(musicListId);
            musicListMusic.setMusicId(musicId);
            musicListMusic.setMusicName(music.getName());
            musicListMusic.setSingerName(music.getSingerName());
            musicListMusic.setAlbumName(music.getAlbumName());
            musicListMusic.setDuration(music.getDuration());
            musicListMusic.setCoverUrl(music.getCoverUrl());

            queryWrapper.eq("music_list_id",musicListId).eq("music_id",musicId);
            if(musicListMusicMapper.selectOne(queryWrapper) !=null){
                throw new ServiceException(ResultCode.MusicList_SONG_EXIST);
            }else {
                musicListMusicMapper.insert(musicListMusic);
            }

//        歌单实体类的歌曲数量加上新增
        musicList.setMusicNums(musicList.getMusicNums() + 1);
        // 更新歌单的封面
        if(musicList.getCoverUrl() == null||Pattern.matches(".*y.qq.com.*",musicList.getCoverUrl())){
            musicList.setCoverUrl(musicListMusic.getCoverUrl());
        }
//        再将此实体类更新至数据库中，失败，抛异常
        if (!updateById(musicList))
            throw new ServiceException(ResultCode.MusicList_UPDATE_FAIL);
//        如果成功，转为MusicListInfoVO并返回
        return MusicListInfoVO.valueOf(musicList);
    }

    /**
     * 删除歌单中的歌曲
     *
     * @param musicId,musicListId
     * @return MusicListInfoVO
     */
    public MusicListInfoVO delMusicToMusicList(Long musicId, Long musicListId) {
//        将之前的redis缓存直接删掉即可
        if (redisTemplate.hasKey(MUSIC_MusicList + musicListId)) {
            if (!redisTemplate.delete(MUSIC_MusicList + musicListId))
                throw new ServiceException(ResultCode.MusicList_UPDATE_FAIL);
        }
        if (redisTemplate.hasKey(MUSIC_MusicList + musicListId + "AllMusic")) {
            if (!redisTemplate.delete(MUSIC_MusicList + musicListId + "AllMusic")) {
                throw new ServiceException(ResultCode.MusicList_UPDATE_FAIL);
            }
        }
//        根据歌单id获取歌单实体类
        MusicList MusicList = this.getById(musicListId);
//        如果查不到，直接抛异常
        if (MusicList == null || MusicList.getDeleteFlag())
            throw new ServiceException(ResultCode.MusicList_GET_FAIL);

            Map<String, Object> columnMap = new HashMap<>();
            columnMap.put("music_list_id", musicListId);
            columnMap.put("music_id", musicId);
            musicListMusicMapper.deleteByMap(columnMap);
//        歌单实体类的歌曲数量减去删除的歌曲数量
        MusicList.setMusicNums(MusicList.getMusicNums() - 1);
//        再将此实体类更新至数据库中，失败，抛异常
        if (!updateById(MusicList))
            throw new ServiceException(ResultCode.MusicList_UPDATE_FAIL);
//        如果成功，转为MusicListInfoVO并返回
        return MusicListInfoVO.valueOf(MusicList);
    }

    /**
     * 一次性增加歌曲的播放量
     *
     * @param musicListId
     * @param count
     * @return
     */
    @Override
    public Boolean addListenCount(Long musicListId, int count) {
        MusicList MusicList = MusicListMapper.selectById(musicListId);
        MusicList.setPlaysNums(MusicList.getPlaysNums() + count);
        if (MusicListMapper.updateById(MusicList) == 1) {
            return true;
        } else {
            throw new ServiceException(ResultCode.MusicList_UPDATE_FAIL);
        }
    }

    /**
     * 随机获取redis中播放量和收藏量前1000的歌单
     */
    public List<MusicListInfoVO> getRandomMusicListFromTop1000(int count) {
        try {
            if (redisTemplate.hasKey(MUSIC_MusicList + "top1000MusicLists")) {
                Set<String> randomMusicListJsonSet = redisTemplate.opsForSet().distinctRandomMembers(MUSIC_MusicList + "top1000MusicLists", count);
                List<MusicListInfoVO> randomMusicListList = new ArrayList<>();
                ObjectMapper objectMapper = new ObjectMapper();
                for (String MusicListJson : randomMusicListJsonSet) {
                    MusicListInfoVO MusicListInfoVO = objectMapper.readValue(MusicListJson, MusicListInfoVO.class);
                    randomMusicListList.add(MusicListInfoVO);
                }
                return randomMusicListList;
            } else {
                addTop1000MusicListsToRedis();
                return getRandomMusicListFromTop1000(count);
            }
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new ServiceException(ResultCode.MusicList_TOP1000_FAIL);
        }
    }

    /**
     * 获取播放量和收藏量前1000的歌单,存入redis中
     */
    public void addTop1000MusicListsToRedis() throws JsonProcessingException {
        List<MusicList> top1000MusicLists = MusicListMapper.getTop1000ByPlaysAndCollections();
        for (MusicList MusicList : top1000MusicLists) {
            MusicListInfoVO musicListInfoVO = MusicListInfoVO.valueOf(MusicList);
            String MusicListJson = new ObjectMapper().writeValueAsString(musicListInfoVO);
            redisTemplate.opsForSet().add(MUSIC_MusicList + "top1000MusicLists", MusicListJson);
        }
    }
}
